package com.file.parser;

import com.file.bo.CSCB;
import com.file.bo.CSCBTran;
import com.file.bo.ResponseData;
import com.file.constant.ErrorCode;
import com.file.parser.BaseDecryptPdfParser;
import com.file.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import technology.tabula.*;
import technology.tabula.detectors.NurminenDetectionAlgorithm;
import technology.tabula.extractors.BasicExtractionAlgorithm;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 丰巢-长沙银行
 */
@Slf4j
public class CSCBPdfParser extends BaseDecryptPdfParser {


    public ResponseData<String> parseCSCBPdfToJson(String daId, String filePath, String pdfPassword) {
        log.info("parseCSCBPdfToJson started, daId:{}", daId);
        String json = null;

        try {
            CSCB CSCB = parseCSCBPdf(filePath, pdfPassword);
            json = JsonUtils.convertObjectToJson(CSCB);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), daId, "", "", "parseCSCBPdfToJson failed", e);
            return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(),
                    ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parseCSCBPdfToJson completed, daId:{}", daId);
        return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    private CSCB parseCSCBPdf(String filePath, String pdfPassword) {
        String pdfHeaderText = parsePdfHeaderText(filePath, pdfPassword);
        String lastPageText = parsePdfPageTextByPageNumber(filePath, getPdfPageNumber(filePath, pdfPassword), pdfPassword);
        boolean hasCounterparty = false;
        if (pdfHeaderText.contains("对方户名")) {
            hasCounterparty = true;
        }
        CSCB CSCB = parseCSCBHeader(pdfHeaderText, lastPageText);
        List<List<String>> rowList = parseFileToRowList(filePath, pdfPassword);
//        for (List<String> cellList : rowList) {
//            System.out.println(cellList);
//        }
        parseListToBO(rowList, CSCB, hasCounterparty);

        return CSCB;
    }

    private void parseListToBO(List<List<String>> rowList, CSCB CSCB, boolean hasCounterparty) {
        List<CSCBTran> CSCBTrans = new ArrayList<>();
        CSCB.setTransactions(CSCBTrans);

        reSetRowList_SplitTranDateAndTranAmout(rowList); //NOSONAR
        reSetRowList_SplitTranDateAndTranAmoutAndaccountBalance(rowList);//NOSONAR
        reSetRowList_SplitCounterpartyAccountAndSummary(rowList);  //NOSONAR
        reSetRowList_SplitTranAmoutAndAccountBalanceAndSummary(rowList); //NOSONAR
        reSetRowList_SplitAccountBalanceAndSummary(rowList); //NOSONAR
        reSetRowList_resetValueForCrossRow(rowList); //NOSONAR

        for (int i = 0; i < rowList.size(); i++) {
            List<String> rows = rowList.get(i);
            if (rows.get(0).matches("\\d{4}-\\d{2}-\\d{2}")) {
                if (hasCounterparty) {
                    if (rows.size() >= 6) {
                        CSCBTran CSCBTran = new CSCBTran();
                        CSCBTran.setTransactionDate(rows.get(0));
                        CSCBTran.setTransactionAmount(rows.get(1));
                        CSCBTran.setAccountBalance(rows.get(2));
                        CSCBTran.setCounterpartyName(rows.get(3));
                        CSCBTran.setCounterpartyAccount(rows.get(4));
                        CSCBTran.setSummary(rows.get(5));
                        CSCBTrans.add(CSCBTran);
                    }

                } else {
                    if (rows.size() >= 4) {
                        CSCBTran CSCBTran = new CSCBTran();
                        CSCBTran.setTransactionDate(rows.get(0));
                        CSCBTran.setTransactionAmount(rows.get(1));
                        CSCBTran.setAccountBalance(rows.get(2));
                        CSCBTran.setCounterpartyName("");
                        CSCBTran.setCounterpartyAccount("");
                        CSCBTran.setSummary(rows.get(3));
                        CSCBTrans.add(CSCBTran);
                    }
                }
            } else {
//                System.out.println(StringUtils.join(rows, ","));
            }
        }
    }

    private void reSetRowList_SplitCounterpartyAccountAndSummary(List<List<String>> rowList) {

        boolean needSplit = false;

//        [交易日期, 交易金额, 账户余额, 对方户名, , 对方账号 摘要/备注]
        for (int i = 0; i < rowList.size(); i++) {
            List<String> rows = rowList.get(i);
            if (rows.size() <= 5) {
                continue;
            }
            if (rows.get(5).contains("对方账号")) {
                if (rows.get(5).equals("对方账号 摘要/备注")) {
                    needSplit = true;
                } else {
                    needSplit = false;
                }
            }
            if (needSplit) {
                String str = rows.get(5);
                if (StringUtils.isEmpty(str)) {
                    continue;
                } else {
                    String summary = str.replaceAll("^\\d{5,35}", "");
                    rows.set(4, str.replaceAll(summary, "").trim());
                    rows.set(5, summary.trim());
                }
            }
        }
    }

    private void reSetRowList_SplitTranDateAndTranAmout(List<List<String>> rowList) {

        boolean needSplitTran = false;

//        [交易日期 交易金额, 账户余额, 对方户名, , 对方账号 摘要/备注]
        for (int i = 0; i < rowList.size(); i++) {
            List<String> rows = rowList.get(i);
            if (rows.size() <= 4) {
                continue;
            }
            if (rows.get(0).contains("交易日期")) {
                if (rows.get(0).equals("交易日期 交易金额")) {
                    needSplitTran = true;
                } else {
                    needSplitTran = false;
                }
            }
            if (needSplitTran) {
                String str = rows.get(0);
                if (StringUtils.isEmpty(str)) {
                    rows.add(1, "");
                } else {
                    String[] array = str.split(" ");
                    rows.set(0, array[0]);
                    rows.add(1, array.length > 1 ? array[1] : "");
                }
            }
        }
    }

    private void reSetRowList_SplitTranDateAndTranAmoutAndaccountBalance(List<List<String>> rowList) {

        boolean needSplit = false;

//        [交易日期 交易金额 账户余额, 对方户名, , 对方账号 摘要/备注]
        for (int i = 0; i < rowList.size(); i++) {
            List<String> rows = rowList.get(i);
            if (rows.size() <= 4) {
                continue;
            }
            if (rows.get(0).contains("交易日期")) {
                if (rows.get(0).equals("交易日期 交易金额 账户余额")) {
                    needSplit = true;
                } else {
                    needSplit = false;
                }
            }
            if (needSplit) {
                String str = rows.get(0);
                if (StringUtils.isEmpty(str)) {
                    rows.add(1, "");
                } else {
                    String[] array = str.split(" ");
                    if (array.length >= 3) {
                        rows.set(0, array[0]);
                        rows.add(1, array[1]);
                        rows.set(2, array[2]);
                    } else if (array.length == 2) {
                        rows.set(0, array[0]);
                        rows.add(1, array[1]);
                    }

                }
            }
        }
    }

    private void reSetRowList_SplitTranAmoutAndAccountBalanceAndSummary(List<List<String>> rowList) {

        boolean needSplit = false;

//        [交易日期 , 交易金额 账户余额 摘要/备注]
        for (int i = 0; i < rowList.size(); i++) {
            List<String> rows = rowList.get(i);
            if (rows.size() < 2) {
                continue;
            }
            if (rows.get(0).contains("交易日期")) {
                if (rows.get(1).equals("交易金额 账户余额 摘要/备注")) {
                    needSplit = true;
                } else {
                    needSplit = false;
                }
            }
            if (needSplit) {
                String str = rows.get(1);
                if (StringUtils.isEmpty(str)) {
                    rows.add(2, "");
                    rows.add(3, "");
                } else {
                    String[] array = str.split(" ");
                    rows.set(1, array[0]);
                    rows.add(2, array.length > 1 ? array[1] : "");
                    rows.add(3, array.length > 2 ? array[2] : "");
                }
            }
        }
    }

    private void reSetRowList_SplitAccountBalanceAndSummary(List<List<String>> rowList) {

        boolean needSplit = false;

//        [交易日期 , 交易金额 , , 账户余额 摘要/备注]
        for (int i = 0; i < rowList.size(); i++) {
            List<String> rows = rowList.get(i);
            if (rows.size() < 4) {
                continue;
            }
            if (rows.get(0).contains("交易日期")) {
                if (rows.get(3).equals("账户余额 摘要/备注")) {
                    needSplit = true;
                } else {
                    needSplit = false;
                }
            }
            if (needSplit) {
                String str = rows.get(3);
                if (StringUtils.isEmpty(str)) {
                    continue;
                } else {
                    String[] array = str.split(" ");
                    rows.set(2, array[0]);
                    rows.set(3, array.length > 1 ? array[1] : "");
                }
            }
        }
    }

    private void reSetRowList_resetValueForCrossRow(List<List<String>> rowList) {
        // 对方户名跨行
        // [, , , 个人其他消费贷款—本, , ]
        // [2019-02-12, +25,000.00, 25,009.94, , 911230101133500002, 贷款业务]
        // [, , , 金, , ]
        for (int i = 1; i < rowList.size() - 1; i++) {
            List<String> rows = rowList.get(i);
            if (rows.size() < 5) {
                continue;
            }
            if (StringUtils.isBlank(rows.get(3)) && !StringUtils.isBlank(rows.get(4))) {
                String counterpartyName = "";
                if (!StringUtils.isBlank(rowList.get(i - 1).get(3))) {
                    counterpartyName = rowList.get(i - 1).get(3);
                }
                if (!StringUtils.isBlank(rowList.get(i + 1).get(3))) {
                    counterpartyName = counterpartyName + rowList.get(i + 1).get(3);
                }
                rows.set(3, counterpartyName);
            }
        }

    }

    private List<List<String>> parseFileToRowList(String filePath, String pdfPassword) {
        //页面所有table的记录，每一个元素代表下面一条记录cellList
        List<List<String>> rowList = new ArrayList<>();

        // 1. 读取文件
        File pdf = new File(filePath);

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf, pdfPassword)) {

            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
            NurminenDetectionAlgorithm detectionAlgorithm = new NurminenDetectionAlgorithm();
            Map<Integer, List<Rectangle>> detectedTables = new HashMap<>();

            // 4. 获取每页的PageIterator
            PageIterator pages = objectExtractor.extract();

            float rTop = 0;
            // 5. 解析每页的Rectangle(table的位置)
            while (pages.hasNext()) {
                Page page = pages.next();
                List<Rectangle> tablesOnPage = detectionAlgorithm.detect(page);
                if (!tablesOnPage.isEmpty()) {
                    detectedTables.put(page.getPageNumber(), tablesOnPage);
                } else {
                    // 适配部分pdf 表格位置识别失败设置默认值
                    Rectangle rectangle = new Rectangle(99, 0, 600, 700);
                    detectedTables.put(page.getPageNumber(), Arrays.asList(rectangle));
                }
            }

            // 6.通过table位置获取表格具体内容
            BasicExtractionAlgorithm bea = new BasicExtractionAlgorithm();

            // 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
            for (Map.Entry<Integer, List<Rectangle>> entry : detectedTables.entrySet()) {
                Page page = objectExtractor.extract(entry.getKey());
                // 默认每页只有一个表格，因此获取第0个rectangle/
                Rectangle rectangle = null; //NOSONAR
                List<Rectangle> rectangleList = entry.getValue();
                for (Rectangle r : rectangleList) {
                    double rectangleHeight = r.getHeight();
                    if (Double.compare(rectangleHeight, 0) > 0) {
//                        System.out.println(r.getTop() + " " + r.getBottom() + " " + r.getLeft() + " " + r.getRight());
                        // 多页pdf如果top大于首页设置为和首页一致,适配部分用例表头位置识别有误
                        if (rTop == 0) {
                            rTop = r.getTop();
                        } else if (r.getTop() > rTop) {
                            r.setTop(rTop);
                        }
                        rectangle = r;
                        break;
                    }
                }
                if (rectangle == null) {
                    throw new RuntimeException("rectangle is null.");
                }
                rectangle.setBottom(rectangle.getBottom() + 700);
                Page area = page.getArea(rectangle);
                List<Table> tableList = null;
                try {
                    tableList = bea.extract(area);
                } catch (Exception e) {
                    log.info("rectangle extract table fail.");
                    continue;
                }
                boolean isTableHeader = false;
                // 如果每页有多个表格，解析每一个table
                for (Table table : tableList) {
                    for (int i = 0; i < table.getRowCount(); i++) {
                        List<String> cellList = new ArrayList<>();//一条记录，每个单元格占一个元素
                        for (int j = 0; j < table.getColCount(); j++) {
                            cellList.add(table.getCell(i, j).getText(false));
                        }
                        if (cellList.get(0).contains("交易日期")) {
                            isTableHeader = true;
                        }
                        // 去除表格之前的cell
                        if (!isTableHeader) {
                            continue;
                        }
                        if (cellList.get(0).contains("承上余额")) {
                            continue;
                        }
                        if (cellList.get(0).contains("本对账单仅供查询使用,不能作为资产证明")) {
                            continue;
                        }
                        if (cellList.get(0).matches("第\\d+页.*")) {
                            continue;
                        }
                        rowList.add(cellList);
                    }
                }

            }
            return rowList;
        } catch (
                Exception e) {
            throw new RuntimeException(e);
        }
    }

    private CSCB parseCSCBHeader(String pdfHeaderText, String lastPageText) {
        CSCB CSCB = new CSCB();

        String accountName = "";
        String idNumber = "";
        String openingBranch = "";
        String customerAccountNumber = "";
        String accountNumber = "";
        String accountType = "";
        String currency = "";
        String totalIncome = "";
        String totalExpense = "";
        String transPeriod = "";

        String transPeriodRegex = "\\d{8}-\\d{8}";
        Pattern pattern = Pattern.compile(transPeriodRegex);
        Matcher matcher = pattern.matcher(pdfHeaderText);
        if (matcher.find()) {
            transPeriod = matcher.group();
        }

        if (pdfHeaderText.contains("账户名称：") && pdfHeaderText.contains("证件号码：")) {
            accountName = pdfHeaderText.substring(pdfHeaderText.indexOf("账户名称：") + 5, pdfHeaderText.indexOf("证件号码：")).trim();
        }

        if (pdfHeaderText.contains("证件号码：") && pdfHeaderText.contains("开户机构：")) {
            idNumber = pdfHeaderText.substring(pdfHeaderText.indexOf("证件号码：") + 5, pdfHeaderText.indexOf("开户机构：")).trim();
        }

        if (pdfHeaderText.contains("开户机构：") && pdfHeaderText.contains("客户账号：")) {
            openingBranch = pdfHeaderText.substring(pdfHeaderText.indexOf("开户机构：") + 5, pdfHeaderText.indexOf("客户账号：")).trim();
        }

        if (pdfHeaderText.contains("客户账号：") && pdfHeaderText.contains(" 账号：")) {
            customerAccountNumber = pdfHeaderText.substring(pdfHeaderText.indexOf("客户账号：") + 5, pdfHeaderText.indexOf(" 账号：")).trim();
        }

        if (pdfHeaderText.contains(" 账号：") && pdfHeaderText.contains("储种：")) {
            accountNumber = pdfHeaderText.substring(pdfHeaderText.indexOf(" 账号：") + 4, pdfHeaderText.indexOf("储种：")).trim();
        }

        if (pdfHeaderText.contains("储种：") && pdfHeaderText.contains("币种：")) {
            accountType = pdfHeaderText.substring(pdfHeaderText.indexOf("储种：") + 3, pdfHeaderText.indexOf("币种：")).trim();
        }

        if (pdfHeaderText.contains("币种：") && pdfHeaderText.contains("交易日期")) {
            currency = pdfHeaderText.substring(pdfHeaderText.indexOf("币种：") + 3, pdfHeaderText.indexOf("交易日期")).trim();
        }
        if (lastPageText.contains("收入合计：")) {
            lastPageText = lastPageText.substring(lastPageText.indexOf("收入合计："));
            totalIncome = lastPageText.substring(lastPageText.indexOf("收入合计：") + 5, lastPageText.indexOf("支出合计")).trim();
            totalExpense = lastPageText.substring(lastPageText.indexOf("支出合计：") + 5, lastPageText.indexOf("第")).trim();
        }

        CSCB.setTransPeriod(transPeriod);
        CSCB.setAccountName(accountName);
        CSCB.setIdNumber(idNumber);
        CSCB.setOpeningBranch(openingBranch);
        CSCB.setCustomerAccountNumber(customerAccountNumber);
        CSCB.setAccountNumber(accountNumber);
        CSCB.setAccountType(accountType);
        CSCB.setCurrency(currency);
        CSCB.setTotalIncome(totalIncome);
        CSCB.setTotalExpense(totalExpense);
        return CSCB;
    }


    public static void main(String[] args) throws IOException {
        CSCBPdfParser CSCBPdfParser = new CSCBPdfParser();
        String json = "";
        json = CSCBPdfParser.parseCSCBPdfToJson("", "D:\\data\\files\\CSCB\\长沙银行-14651X-6.pdf", "14651X").getData();
        System.out.println(json);
//        json = CSCBPdfParser.parseCSCBPdfToJson("", "D:\\data\\file\\beehive-cscb\\14651X.pdf", "14651X").getData();
        System.out.println(json);
//        json = CSCBPdfParser.parseCSCBPdfToJson("", "D:\\data\\files\\CSCB\\长沙银行-14651X-2.pdf", "14651X").getData();
//        System.out.println(json);
//
//        json = CSCBPdfParser.parseCSCBPdfToJson("", "D:\\data\\files\\CSCB\\长沙银行-14651X-3.pdf", "14651X").getData();
//        System.out.println(json);
//
//        json = CSCBPdfParser.parseCSCBPdfToJson("", "D:\\data\\files\\CSCB\\长沙银行-14651X-4.pdf", "14651X").getData();
//        System.out.println(json);
//
//        json = CSCBPdfParser.parseCSCBPdfToJson("", "D:\\data\\files\\CSCB\\长沙银行-298486-5.pdf", "298486").getData();
//        System.out.println(json);
    }
}